package com.gitee.feizns.dynamic;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <b>java8 流操作工具类</b>
 *
 * @see Stream
 * @author feizns
 * @since 2019/6/9 0009
 */
public abstract class StreamUtils {

    /**
     * <b>从两个参数中选择第二个值留下</b>
     * <p>
     *     类似lambda表达式： (v1, v2) -> v2 <br/>
     *     {@code BinaryOperator<T> p = (v1, v2) -> v2;}<br/>
     *     {@code BinaryOperator<T> p = StreamUtils::leaveSecond;}
     * </p>
     * <p>
     *     语义化方法，仅仅作为方法引用时，使代码具有更好的可读性
     * </p>
     * @param t 第一个参数
     * @param t2 第二个参数
     * @param <T> 参数类型
     * @return 第二给参数
     */
    public static <T> T leaveSecond(T t, T t2) {
        return t2;
    }

    /**
     * <b>不对参数做任何改变直接返回</b>
     * <p>语义化方法，仅仅作为方法引用时，使代码具有更好的可读性</p>
     * <p>
     *     类似lambda表达式：{@code v -> v}
     * </p>
     * <pre>{@code
     *  Function<T> p = v -> v;
     *  Function<T> p = StreamUtils::noChange;
     * }</pre>
     * @param original 参数（没有意义）
     * @return 直接返回参数original
     */
    public static <R> R noChange(R original) {
        return original;
    }

    /**
     * <b>直接返回true</b>
     *
     * <p>
     *     类似lambda表达式：{@code t -> true}<br/>
     * </p>
     * <pre>{@code
     *  Predicate<T> p = v -> true;
     *  Predicate<T> p = StreamUtils::allTrue;
     * }</pre>
     * @see #allTrue(Object)
     * @see #allTrue(Object, Object)
     * @see #alwaysTure(Object)
     * @see #alwaysTure(Object, Object)
     * @apiNote 语义化方法，仅仅作为方法引用时，使代码具有更好的可读性
     * @param t 参数（没有意义）
     * @param <T> t的参数类型
     * @return 直接返回参数true
     */
    public static <T> boolean allTrue(T t) {
        return true;
    }

    /**
     * <b>直接返回true</b>
     *
     * <p>
     *     类似lambda表达式：{@code t -> true}<br/>
     * </p>
     * <pre>{@code
     *  Predicate<T> p = v -> true;
     *  Predicate<T> p = StreamUtils::alwaysTure;
     * }</pre>
     * @see #allTrue(Object)
     * @see #allTrue(Object, Object)
     * @see #alwaysTure(Object)
     * @see #alwaysTure(Object, Object)
     * @apiNote 语义化方法，仅仅作为方法引用时，使代码具有更好的可读性
     * @param t 参数（没有意义）
     * @param <T> t的参数类型
     * @return 返回true
     */
    public static <T> boolean alwaysTure(T t) {
        return true;
    }

    /**
     * <b>直接返回true</b>
     * <p>语义化方法，仅仅作为方法引用时，使代码具有更好的可读性</p>
     * <p>
     *     类似lambda表达式：{@code (t, v) -> true}<br/>
     * </p>
     * <pre>{@code
     *  Predicate<T> p = (t, v) -> true;
     *  Predicate<T> p = StreamUtils::alwaysTure;
     * }</pre>
     * @see #allTrue(Object)
     * @see #allTrue(Object, Object)
     * @see #alwaysTure(Object)
     * @see #alwaysTure(Object, Object)
     * @apiNote 语义化方法，仅仅作为方法引用时，使代码具有更好的可读性
     * @param t 参数（没有意义）
     * @param <T> t的参数类型
     * @return 返回true
     */
    public static <T, U> boolean allTrue(T t, U u) {
        return true;
    }

    /**
     * <b>直接返回true</b>
     *
     * <p>
     *     类似lambda表达式：{@code (t, u) -> true}<br/>
     * </p>
     * <pre>{@code
     *  Predicate<T> p = (t, u) -> true;
     *  Predicate<T> p = StreamUtils::alwaysTure;
     * }</pre>
     * @see #allTrue(Object)
     * @see #allTrue(Object, Object)
     * @see #alwaysTure(Object)
     * @see #alwaysTure(Object, Object)
     * @apiNote 语义化方法，仅仅作为方法引用时，使代码具有更好的可读性
     * @param t 第一参数（没有意义）
     * @param u 第二个参数（没有意义）
     * @param <T> t的参数类型
     * @param <U> u的参数类型
     * @return 返回true
     */
    public static <T, U> boolean alwaysTure(T t, U u) {
        return true;
    }

    /**
     * <b>直接返回false</b>
     *
     * <p>
     *     类似lambda表达式：{@code t -> false}<br/>
     * </p>
     * <pre>{@code
     *  Predicate<T> p = v -> false;
     *  Predicate<T> p = StreamUtils::allFalse;
     * }</pre>
     * @see #allFalse(Object)
     * @see #allFalse(Object, Object)
     * @see #alwaysFalse(Object)
     * @see #alwaysFalse(Object, Object)
     * @apiNote 语义化方法，仅仅作为方法引用时，使代码具有更好的可读性
     * @param t 参数（没有意义）
     * @param <T> t的参数类型
     * @return 返回false
     */
    public static <T> boolean allFalse(T t) {
        return false;
    }

    /**
     * <b>直接返回false</b>
     *
     * <p>
     *     类似lambda表达式：{@code (v, u) -> false }<br/>
     * </p>
     * <pre>{@code
     *  BiPredicate<T, U> p = (v, u) -> false;
     *  BiPredicate<T, U> p = StreamUtils::allFalse;
     * }</pre>
     * @see #allFalse(Object)
     * @see #allFalse(Object, Object)
     * @see #alwaysFalse(Object)
     * @see #alwaysFalse(Object, Object)
     * @apiNote 语义化方法，仅仅作为方法引用时，使代码具有更好的可读性
     * @param t 第一个参数（没有意义）
     * @param u 第二个参数（没有意义）
     * @param <T> t的参数类型
     * @param <U> u的参数类型
     * @return 返回false
     */
    public static <T, U> boolean allFalse(T t, U u) {
        return false;
    }

    /**
     * <b>直接返回false</b>
     *
     * <p>
     *     类似lambda表达式：{@code v -> false }<br/>
     * </p>
     * <pre>{@code
     *  Predicate<T> p = v -> false;
     *  Predicate<T> p = StreamUtils::alwaysFalse;
     * }</pre>
     * @see #allFalse(Object)
     * @see #allFalse(Object, Object)
     * @see #alwaysFalse(Object)
     * @see #alwaysFalse(Object, Object)
     * @apiNote 语义化方法，仅仅作为方法引用时，使代码具有更好的可读性
     * @param t 参数（没有意义）
     * @param <T> t的参数类型
     * @return 返回false
     */
    public static <T> boolean alwaysFalse(T t) {
        return false;
    }

    /**
     * <b>直接返回false</b>
     *
     * <p>
     *     类似lambda表达式：{@code (v, u) -> false }<br/>
     * </p>
     * <pre>{@code
     *  BiPredicate<T, U> p = (v, u) -> false;
     *  BiPredicate<T, U> p = StreamUtils::alwaysFalse;
     * }</pre>
     * @see #allFalse(Object)
     * @see #allFalse(Object, Object)
     * @see #alwaysFalse(Object)
     * @see #alwaysFalse(Object, Object)
     * @apiNote 语义化方法，仅仅作为方法引用时，使代码具有更好的可读性
     * @param t 参数（没有意义）
     * @param u 参数（没有意义）
     * @param <T> t的参数类型
     * @param <U> u的参数类型
     * @return 返回false
     */
    public static <T, U> boolean alwaysFalse(T t, U u) {
        return false;
    }

    /**
     * <b>创建一个拥有指定数量的流构建器的流</b>
     * @see Stream.Builder
     * @param size 流大小
     * @param <T> 流构建类型
     * @return 返回一个装有size个流构建器的流
     */
    public static <T> Stream<Stream.Builder<T>> streamBuilders(int size) {
        Stream.Builder<Stream.Builder<T>> builder = Stream.builder();
        for (int i = 0; i < size; i++) {
            builder.add(Stream.builder());
        }
        return builder.build();
    }

    /**
     * <b>将参数stream复制size份</b>
     * <p>
     *     java8中流被设计成仅能使用一次，用于需要重新利用的同一个流时。
     * </p>
     * @param stream 需要复制的流
     * @param size 需要复制多少份
     * @param <T> stream中数据的类型
     * @return 返回一个拥有size份流的流对象
     */
    public static <T> Stream<Stream<T>> copy(Stream<T> stream, int size) {
        Stream<Stream.Builder<T>> streamBuilders = streamBuilders(size);
        List<T> streamList = stream.collect(Collectors.toList());
        return streamBuilders.map(item -> {
            streamList.forEach(item::add);
            return item.build();
        });
    }

    /**
     * <b>将字符串转换为字节流</b>
     *
     * @param str 字符串
     * @return 包含str中所有字符的流
     */
    public static Stream<Character> stream(String str) {
        return str != null ? str.chars().mapToObj(ch -> (char) ch) : Stream.empty();
    }

    /**
     * <b>将集合转换为流，集合为null则转换为空流</b>
     *
     * @param collection 集合
     * @param <T> 集合中元素的类型
     * @return 包含原collection中所有元素的流
     */
    public static <T> Stream<T> stream(Collection<T> collection) {
        return collection != null ? collection.stream() : Stream.empty();
    }

    /**
     * <b>将一个可迭代的对象转换为流，集合为null则转换为空流</b>
     *
     * @param iterable 集合
     * @param <T> 集合中元素的类型
     * @return 包含原collection中所有元素的流
     */
    public static <T> Stream<T> stream(Iterable<T> iterable) {
        Stream.Builder<T> builder = Stream.builder();
        iterable.forEach(builder::add);
        return builder.build();
    }

    /**
     * <b>将一个未知的对象构建成流</b>
     * <p>
     *     如果{@code elements}是一个数组则将数组中的元素装配到流中<br/>
     *     如果{@code elements}是一个集合则将集合中的元素装配到流中<br/>
     *     如果{@code elements}是普通对象则将该对象装配到流中
     * </p>
     * <pre>{@code
     *
     * }</pre>
     * @param elements 未知类型的对象
     * @return 返回装有元素的流
     */
    @SuppressWarnings("unchecked")
    public static Stream<Object> stream(Object elements) {
        Stream.Builder<Object> builder = Stream.builder();
        if ( elements != null ) {
            Class<?> type = elements.getClass();
            if ( type.isArray() ) {
                for (int i = 0; i < Array.getLength(elements); i++)
                    builder.add(Array.get(elements, i));
            } else if( Iterable.class.isAssignableFrom(type) ) {
                ((Iterable<Object>) elements).forEach(builder::add);
            } else {
                builder.add(elements);
            }
        }
        return builder.build();
    }

}
